﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Runtime.Serialization;
using System.Xml.Linq;

namespace GNF.Geo
{
    /// <summary>
    /// Holds a GPS Data of:
    /// Longitude,Altitude,Latitude,the available satellites
    /// </summary>
    [DataContract(Namespace = "urn:GNF.Geo")]
    public class GPSData : INotifyPropertyChanged, GNF.Geo.ISaveableToXml
    {
        #region private members

        /// <summary>
        /// the longitude in UTS 
        /// </summary>
        protected double _longitude;

        /// <summary>
        /// the latitude in UTS 
        /// </summary>
        protected double _latitude;

        /// <summary>
        /// the altitude in UTS 
        /// </summary>
        protected double _altitude;

        /// <summary>
        /// the number of satellites that the reading came from (indicate of accurate is the Data
        /// </summary>
        protected double _availableSatellites;

        /// <summary>
        /// the date the data was taken on
        /// </summary>
        protected DateTime _takenAt;

        #endregion

        #region public properties

        [DataMember(Order = 0, IsRequired = true)]
        public double Longitude 
        {
            get
            {
                return _longitude;
            }
            set
            {
                _longitude = value;
                OnPropertyChanged("Longitude");
            }
        }

        [DataMember(Order = 1, IsRequired = true)]
        public double Latitude
        {
            get
            {
                return _latitude;
            }
            set
            {
                _latitude = value;
                OnPropertyChanged("Latitude");
            }
        }

        [DataMember(Order = 2, IsRequired = false)]
        public double Altitude
        {
            get
            {
                return _altitude;
            }
            set
            {
                _altitude = value;
                OnPropertyChanged("Altitude");
            }
        }

        [DataMember(Order = 3, IsRequired = false)]
        public double AvailableSatellites
        {
            get
            {
                return _availableSatellites;
            }
            set
            {
                _availableSatellites = value;
                OnPropertyChanged("AvailableSatellites");
            }
        }

        [DataMember(Order = 4,IsRequired = false)]
        public DateTime TakenAt
        {
            get
            {
                return _takenAt;
            }
            set
            {
                _takenAt = value;
                OnPropertyChanged("TakenAt");
            }
        }

        #endregion

        #region construction and initialize

        /// <summary>
        /// create a new GPS Data with the values given
        /// </summary>
        /// <param name="longitude">the new longitude of the Data</param>
        /// <param name="latitude">the new latitude of the Data</param>
        /// <param name="altitude">the new altitude of the Data</param>
        /// <param name="availableSatellites">the new availableSatellites of the Data</param>
        public GPSData(double longitude, double latitude, double altitude, double availableSatellites)
        {
            Longitude = longitude;
            Latitude =latitude;
            Altitude = altitude;
            AvailableSatellites = availableSatellites;
            TakenAt = DateTime.Now;
        }

        /// <summary>
        /// defualt constructor
        /// </summary>
        public GPSData() { TakenAt = DateTime.Now; }


        /// <summary>
        /// copy constructor, create a deep copy of GPS Data,
        /// </summary>
        /// <param name="copy"></param>
        public GPSData(GPSData copy)
        {
            if (copy == null)
            {
                throw new Exception("Invalid Parameter to GPS Data Copy constructor");
            }
            Longitude = copy.Longitude;
            Latitude = copy.Latitude;
            Altitude = copy.Altitude;
            AvailableSatellites = copy.AvailableSatellites;
            TakenAt = copy.TakenAt;
        }

        #endregion

        #region public methods

        /// <summary>
        /// return a string that show all the Data,
        /// the string format is:
        /// GPS Data: Longitude: {0} ,Latitude: {1} ,Altitude: {2} ,Sat: {3}
        /// </summary>
        /// <returns>the formated string</returns>
        public override string ToString()
        {
            return string.Format("GPS Data: Longitude: {0} ,Latitude: {1} ,Altitude: {2} ,Sat: {3}", Longitude, Latitude, Altitude, AvailableSatellites);
        }

        /// <summary>
        /// copy the data from one GPSData class to another
        /// </summary>
        /// <param name="newData">the source of the new data</param>
        public void ChangeTo(GPSData newData)
        {
            if (newData == null)
            {
                throw new Exception("GPS Data: trying to copy an empty GPS Data");
            }

            Longitude = newData.Longitude;
            Latitude = newData.Latitude;
            Altitude = newData.Altitude;
            AvailableSatellites = newData.AvailableSatellites;
            TakenAt = newData.TakenAt;
        }

        #endregion

        #region INotifyPropertyChanged Members

        /// <summary>
        /// a defualt event of WPF that help ensure a correct data binding 
        /// </summary>
        /// <param name="a_property">the property that changed</param>
        void OnPropertyChanged(string a_property)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(a_property));
        }

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion

        #region ISaveableToXml Members

        /// <summary>
        /// save a GPS Data class to an Xml Element
        /// </summary>
        /// <param name="data">the GPS data</param>
        /// <returns>the xelement that represent the GPS Data class</returns>
        public XElement Save()
        {
            return new XElement("GPSData", new XAttribute("Longitude", _longitude), new XAttribute("Latitude", _latitude),
                                          new XAttribute("Altitude", _altitude), new XAttribute("AvailableSatellites", _availableSatellites)
                                          );
        }

        public XElement Save(DateTime time)
        {
            var e = Save();
            e.Add(new XAttribute("TimeTaken", time));
            return e;
        }

        /// <summary>
        /// return a new 
        /// </summary>
        /// <param name="elem"></param>
        /// <returns></returns>
        public GPSData Load(XElement elem)
        {
            try
            {
                return new GPSData(double.Parse(elem.Attribute("Longitude").Value), double.Parse(elem.Attribute("Longitude").Value),
                                   double.Parse(elem.Attribute("Longitude").Value), double.Parse(elem.Attribute("Longitude").Value));
            }
            catch (Exception e)
            {
                throw new Exception("Trying to load an unkown class",e);
            }
        }
        #endregion
    }
}
